#ifndef _game_ActionDispatcher_hpp_
#define _game_ActionDispatcher_hpp_

/** \file ActionDispatcher.hpp
 *  \author Adrian Schweizer
 *  \created  $Fr 13 Jul 01:03:15 am CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $Mo 11 Feb 09:21:47 pm CET 2008 schwadri@SchwadriLaptop.local$
 */

//#include <iostream>
//#include <iomanip>

#include <boost/mpl/find.hpp>
#include <boost/static_assert.hpp>

#include "../core/bitstream.hpp"
#include "../core/bitstream_archive.hpp"
#include "../network/Types.hpp"
#include "../network/Client.hpp"


#include "Common.hpp"
//#include "GameController.hpp"
#include "Actions.hpp"

namespace game {

    //forward decl
    class GameController;

    namespace action {

        using core::bitstream_iarchive;
        using core::bitstream_oarchive;

        class Dispatcher
        {
        private:

            struct ActionHandler;

            /** \internal
             *  \brief meta function template to determine the action id from a type at compile time
             *
             *  \par Example
             *  \code
             *  std::cout << get_action_id<ChangeGameName>::value << std::endl;
             *  \endcode
             */
            template<class Action>
                struct get_action_id
                {
                private:
                    typedef typename boost::mpl::find<  GameActions,
                                                        Action>::type                          iter;

                    //if this assertion fails, its very likely that you forgot to put the action into the ActionVector
                    BOOST_STATIC_ASSERT((!boost::is_same<iter,typename boost::mpl::end<GameActions>::type>::type::value));
                public:
                    typedef get_action_id<Action>   type;
                    static const uint8  value   =   iter::pos::value+network::ID_GAME_ACTION_BEGIN;
                };

            GameController&     m_game_controller;
            bool                m_hosting;
            bool                IsServer() const;
        public:
            /** \brief ctor
             *
             *  \param[in] gc reference to the game controller
             */
            Dispatcher(GameController& gc);

            /** \brief handle a received game action
             *
             *  \param[in] in       A bitstream containing the data used to deserialize the action object
             *  \param[in] source   The id of the client who sent this action (seen from a client this is always the host/server.
             *                      and if it isn't the action is ignored). This is NOT the same as the client who created the Action.
             *  \return \a true if the action was dispatched successfully. \a false otherwise
             */
            bool Dispatch(core::bitstream& in, network::Client::Id source);

            /** \brief dispatch a game action
             *
             *  \param[in] action a reference to the action that should be dispatched
             *  \param[in] source   The id of the client who sent this action.
             *  \return \a true if the action was dispatched successfully. \a false otherwise
             */
            template<class Action>
                bool Dispatch(Action & action ,network::Client::Id source);

        //private:
            /*template<class Action>
                void SendTo(Action & ac, network::Client::Id destination);*/

            template<class Action>
                void Send(const Action & ac);

            template<class Action>
                void SendToServer(Action & ac);
        private:
            void Send(core::bitstream& b, network::receiver_mask receivers);
        };

        //impl
        template<class Action>
            bool Dispatcher::Dispatch(Action & action ,network::Client::Id source)
            {
                if(IsServer())
                {
                    if(!action.Validate(m_game_controller,source))
                        return false;

                    if(!action.Execute(m_game_controller,source))
                        return false;

                    Send(action);
                }
                else
                {
                    if(source == network::Client::SERVER)
                    {
                        if(!action.Validate(m_game_controller,source))
                            return false;

                        if(!action.Execute(m_game_controller,source))
                            return false;
                    }
                    else
                    {
                        if(!action.Validate(m_game_controller,source))
                            return false;
                        SendToServer(action);
                    }
                }
                return true;
            }
        
        template<class Action>
            void Dispatcher::Send(const Action & ac)
            {
                core::bitstream  bout;
                bitstream_oarchive  out(bout);
                uint8 id = get_action_id<Action>::value;
                bout.write_aligned(id);
                out << ac;
                //Send to receivers as specified in the receiver mask
                Send(bout,ac.Receivers());
            }
        
        template<class Action>
            void Dispatcher::SendToServer(Action & ac)
            {
                core::bitstream  bout;
                bitstream_oarchive  out(bout);
                uint8 id = get_action_id<Action>::value;
                bout.write_aligned(id);
                out << ac;
                //send to server
                Send(bout,ac.Receivers());
            }
        



        } // namespace action

} // namespace game

#endif // _game_ActionDispatcher_hpp_
